#include "net.h"

#include <sys/time.h>
#include <string.h>

uint16_t init_udp(p_socket_io sock) {
	int ret;
	int on = 1;
	int reuse=1;
	uint16_t res = NO_ERR;
	if((sock->fd = socket(PF_INET,SOCK_DGRAM,0)) == -1)
	{
		printf("error in socket\n");
		return ERR_SOCKET_CREATE;
	}
	struct in_addr loc_i = {
		.s_addr = sock->local_addr.sin_addr.s_addr
	};
	struct sockaddr_in loc;
	memcpy(&loc, &sock->local_addr, sizeof(sock->local_addr));
	loc.sin_addr.s_addr=INADDR_ANY;
	if(bind(sock->fd,(const struct sockaddr *)&loc, sizeof(loc))==-1)
	{
		printf("error in binding:%s\n", inet_ntoa(sock->local_addr.sin_addr));
		//return ERR_SOCKET_BIND;
	}
	printf("binding:%s\n", inet_ntoa(loc.sin_addr));

	setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse,sizeof(int));
	switch (sock->udp_type) {
		case SOCKET_IO_TYPE_UDP_S:
			break;
		case SOCKET_IO_TYPE_UDP_M:
			if (SOCKET_FLOW_TYPE_CLIENT == sock->flow_type) {
				ret = setsockopt(sock->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &sock->loopback, sizeof(sock->loopback));
				if (ret < 0) {
					printf("setsockopt broadcaset error!!!\n");
					perror("setsockopte");
					res = ERR_SOCKET_OPT_1;
					break;
				}
				if (setsockopt(sock->fd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&loc_i, sizeof(loc_i)) < 0) {
					perror("setting local interface");
					res = ERR_SOCKET_OPT_1;
					break;
				}
			} else if (SOCKET_FLOW_TYPE_SERVER == sock->flow_type) {
				int reuse=1;
				if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0) {
					perror("setting SO_REUSEADDR");
					res = ERR_SOCKET_OPT_2;
					break;
				}
				ret = setsockopt(sock->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&(sock->mreq), sizeof(sock->mreq));
				if (ret < 0) {
					res = ERR_SOCKET_OPT_2;
					perror("Setting IP_MULTICAST_IF error:");
				}
			}
			break;
		case SOCKET_IO_TYPE_UDP_B:
			ret = setsockopt(sock->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
			if (ret < 0) {
				res = ERR_SOCKET_OPT_3;
			}
			//sock->remote_addr.sin_addr.s_addr = INADDR_BROADCAST;
			break;
		default:
			break;
	}
	switch (sock->sopt)
	{
		case sockopt_rcvtimeo:
			ret = setsockopt(sock->fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&(sock->recv_timeout), sizeof(struct timeval));
			if (ret < 0)
			{
				res = ERR_SOCKET_OPT_4;
			}
		break;
		default:
		break;
	}
	if (res != NO_ERR) {
		close(sock->fd);
	}
	return res;
}

void init_sockaddr_in(struct sockaddr_in* addr, uint32_t ip, uint16_t port, uint8_t type)
{
	if (!addr) return;
	memset(addr,0,sizeof(*addr));
	addr->sin_family = AF_INET;
	addr->sin_port = htons(port);
	addr->sin_addr.s_addr = ip;
}

void init_sockaddr_in_2(struct sockaddr_in* addr, const char* ip, uint16_t port, uint8_t type)
{
	init_sockaddr_in(addr, inet_addr(ip), port, type);
}

void init_ip_mreq(struct ip_mreq* mreq, uint32_t loc, uint32_t mcast)
{
	if (!mreq) return;
	mreq->imr_multiaddr.s_addr = mcast;
	mreq->imr_interface.s_addr = loc;
}

void init_ip_mreq_2(struct ip_mreq* mreq, const char* loc, const char* mcast)
{
	init_ip_mreq(mreq, inet_addr(loc), inet_addr(mcast));
}


void update_input_state(p_main_flow mflow, uint8_t pkt, uint16_t size,  uint16_t err) 
{
	if (err == NO_ERR)
	{
		mflow->m_flow_state.tx_pkt += pkt;
		mflow->m_flow_state.tx_byte += size;
	} else
	{
		mflow->m_flow_state.err_tx_pkt += pkt;
		mflow->m_flow_state.err_tx_byte += size;
	}
}

void update_output_state(p_main_flow mflow, uint8_t pkt, uint16_t size,  uint16_t err) 
{
	if (!mflow) return;
	if (err == NO_ERR)
	{
		mflow->m_flow_state.rx_pkt += pkt;
		mflow->m_flow_state.rx_byte += size;
	} else
	{
		mflow->m_flow_state.err_rx_pkt += pkt;
		mflow->m_flow_state.err_rx_byte += size;
	}
	strcpy(mflow->m_flow_state.sstate.remote_ip, inet_ntoa(mflow->m_socket_io.remote_addr.sin_addr));
}

